package com.sc.nft.config.lianlian;

import com.sc.nft.exception.GlobalRunTimeException;
import lombok.experimental.UtilityClass;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;

import static com.sc.nft.config.lianlian.LlConfig.*;

@UtilityClass
public class LLianPayClient {
    private static Logger log = LoggerFactory.getLogger(LLianPayClient.class);

    public static final String CHARSET="UTF-8";

    public String sendRequest(String url ,String method, String body) {
        String sign = sign(body);
        String requestUrl = url+method;
        HttpPost post = new HttpPost(requestUrl);
        post.setConfig(RequestConfig.custom().setConnectTimeout(10000).setSocketTimeout(10000).build());
        post.setHeader("Content-Type", "application/json;charset=utf-8");
        post.setHeader("Signature-Type", "RSA");
        post.setHeader("Signature-Data", sign);
        try {
            StringEntity stringEntity = new StringEntity(body, "UTF-8");
            stringEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING, "UTF-8"));
            // 设置请求主体
            post.setEntity(stringEntity);
            // 发起交易
            HttpResponse resp = LLianPayHttpClient.getLLianPayHttpClient().execute(post);
            int ret = resp.getStatusLine().getStatusCode();
            if (ret == HttpStatus.SC_OK) {
                // 响应分析
                HttpEntity entity = resp.getEntity();

                BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
                StringBuffer responseString = new StringBuffer();
                String result = br.readLine();
                while (result != null) {
                    responseString.append(result);
                    result = br.readLine();
                }
                String resSignatureData = "";
                Header headers[] = resp.getAllHeaders();
                int i = 0;
                while (i < headers.length) {
                    if ("Signature-Data".equals(headers[i].getName())) {
                        resSignatureData = headers[i].getValue();
                    }
                    i++;
                }
                log.info(String.format("响应结果：%s", responseString));
                if (!"".equalsIgnoreCase(resSignatureData)) {
                    log.info(String.format("响应签名：%s", resSignatureData));
                    boolean checksign = checkSign(responseString.toString(), resSignatureData);
                    if (!checksign) {
                        log.error("返回响应验证签名异常，请核实！");
                        //throw new RuntimeException("返回响应验证签名异常");
                    } else {
                        log.info(String.format("响应验签通过！"));
                    }
                }
                return responseString.toString();
            }
            throw new RuntimeException("请求结果异常，响应状态码为：" + ret);
        } catch (ConnectTimeoutException cte) {
            log.error(cte.getMessage());
            throw new RuntimeException(cte);
        } catch (SocketTimeoutException cte) {
            log.error(cte.getMessage());
            throw new RuntimeException(cte);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }
    /**
     * 签名
     * @return
     */
    private String sign(String content){
        try {
            String hash = Md5Algorithm.getInstance().md5Digest(content.getBytes(StandardCharsets.UTF_8));
            return RSASign.getInstance().sign(privateKey, hash);
        } catch (Exception e) {
            log.error("签名失败,{}", e.getMessage());
        }
        throw new GlobalRunTimeException("签名失败");
    }

    /**
     * 签名验证
     *
     * @param sign_str    ：源串
     * @param signed_str  ：签名结果串
     * @return
     */
    public boolean checkSign( String sign_str, String signed_str) {
        try {
            String hash = Md5Algorithm.getInstance().md5Digest(sign_str.getBytes(StandardCharsets.UTF_8));
            return RSASign.getInstance().checksign(LlConfig.llPublicKey, hash, signed_str);
        } catch (Exception e) {
            log.error("签名验证异常,{}", e.getMessage());
        }
        return false;
    }

    /**
     * 公钥加密的方法
     * @param source
     * @return
     * @throws Exception
     */
    public  String encrypt(String source)   {
        String epStr=null ;
        try{
            BASE64Decoder b64d = new BASE64Decoder();
            log.info("加密前数据,{}",source);
            byte[] keyByte = b64d.decodeBuffer(llPublicKey);
            log.info("加密使用证书,{}",llPublicKey);
            X509EncodedKeySpec x509ek = new X509EncodedKeySpec(keyByte);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509ek);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] sbt = source.getBytes(CHARSET); // //本地测试可以使用这个
            byte[] epByte = cipher.doFinal(sbt);
            BASE64Encoder encoder = new BASE64Encoder();
            epStr = encoder.encode(epByte);
            log.info("加密后数据,{}",epStr);
        } catch (Exception e){
            log.error("连连加密失败:",e);
            throw new GlobalRunTimeException("连连支付：加密失败");
        }

        return epStr;
    }
}
